package com.ruicar.afs.cloud.afscase.dispatch.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruicar.afs.cloud.afscase.common.utils.Const;
import com.ruicar.afs.cloud.afscase.dispatch.DispatchContext;
import com.ruicar.afs.cloud.afscase.dispatch.component.DispatchComponent;
import com.ruicar.afs.cloud.afscase.dispatch.service.DispatchBaseServcie;
import com.ruicar.afs.cloud.afscase.dispatch.service.IDispatchServcie;
import com.ruicar.afs.cloud.afscase.paramconfmanagement.entity.CaseConfParam;
import com.ruicar.afs.cloud.afscase.paramconfmanagement.service.CaseConfParamService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.text.MessageFormat;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;

/**
 * @author happysayhello
 */
@Service
@Slf4j
public class DispatchServcieImpl implements IDispatchServcie {

    @Autowired
    private CaseConfParamService caseConfParamService;
    private static final ExecutorService EXECUTOR_SERVICE = new ThreadPoolExecutor(5, 10, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>(1024));
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private static final String LOCK_FLOW_EXECUTE_FLAG = "LOCK_FLOW_EXECUTE_FLAG";
    private static final Integer INIT_TIME_OUT_SECONDS = 300; // 初始锁定时间

    public boolean tryLock(){
        return stringRedisTemplate.opsForValue().setIfAbsent(LOCK_FLOW_EXECUTE_FLAG, LOCK_FLOW_EXECUTE_FLAG, INIT_TIME_OUT_SECONDS, TimeUnit.SECONDS);
    }

    public void unLock(){
        stringRedisTemplate.opsForValue().set(LOCK_FLOW_EXECUTE_FLAG, LOCK_FLOW_EXECUTE_FLAG, 1, TimeUnit.SECONDS);
    }

    @SuppressWarnings("rawtypes")
    public void excutorProcess(DispatchContext context) {

        Optional<CaseConfParam> confParamOptional = Optional.ofNullable(caseConfParamService
                .getOne(Wrappers.<CaseConfParam>query().lambda().eq(CaseConfParam::getParamType, Const.PARAMS_AUTO_DISPATCH_FLAG)));
        boolean flag = confParamOptional.isPresent() ? Boolean.valueOf(confParamOptional.get().getParamValue()) : false;
        log.info("自动分单参数为: {}", flag);
        if (!flag) // 是否开启自动分单
            return;
        if (! tryLock()) {
            log.info("分单任务正在执行中，本次任务取消执行 [{}] ", Thread.currentThread().getName());
            return;
        }
        try {
            long startTime = System.currentTimeMillis();
            log.info("分单任务开始");
            CountDownLatch countDownLatch = new CountDownLatch(DispatchComponent.getServicesMap().keySet().size());
            DispatchComponent.getServicesMap().entrySet().stream().forEach(services -> {
                List<DispatchBaseServcie> servicesList = services.getValue();
                DispatchContext newContext = new DispatchContext<>();
                EXECUTOR_SERVICE.submit(() -> {
                    try {
                        servicesList.forEach(s -> {
                            try {
                                log.info("执行分单前置任务 [{}] ", s.getName());
                                s.process(newContext);
                            } catch (Exception e) {
                                log.error("分单前置任务 [{}] 执行失败", s.getName(), e);
                                Assert.isTrue(s.isSkip(), MessageFormat.format(" [{0}] 任务执行失败，不跳过本次执行", s.getName()));
                            }
                        });
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    } finally {
                        countDownLatch.countDown();
                    }

                });
                newContext.clear();
            });
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                log.error("分单任务执行失败", e);
            }

            log.info("本次分单任务执行完毕,耗时：{}", System.currentTimeMillis() - startTime);
        }catch (Exception e){
            log.error("分单任务执行失败",e);
        }finally {
            unLock();
        }
    }

}
